/*
 * Administrador de líneas de comunicación. Este proceso se encarga de
 * interceptar los mensajes salientes. Si un mensaje requiere una línea
 * de comunicación que aún no fue establecida, realiza la conexión, lanza los
 * procesos emisor y receptor, y pone el mensaje en la cola de salida.
 *
 *  Created on: Jan 28, 2010
 *      Author: jgrande
 */

#include "BrokerCliente.h"

using std::string;

using middleware::common::Direccion;
using middleware::common::DireccionTCP;
using middleware::common::PDUPuntoAPunto;

static bool terminar = false;

void alc_sigint_handler(int signal) {
	logger << LogType::LOG_DEBUG_0 << "terminando proceso..." <<
			logger.endl;
	terminar = true;
}

void _brokerClienteALC();

pid_t brokerClienteALC() {
	pid_t pid;

	if((pid=fork())==0) {
		logger.set_pname("Middleware/ALC");
		Utiles::registrar_signal(SIGINT, alc_sigint_handler);
		setpgrp();
		_brokerClienteALC();
		exit(0);
	}

	return pid;
}

void terminarHijos() {
	logger << LogType::LOG_DEBUG_0 << "eliminando procesos creados por el ALC"
			<< logger.endl;
	Utiles::registrar_signal(SIGINT, SIG_IGN);
	if(kill(0, SIGINT)!=-1) {
		pid_t ret;
		while((ret=wait(NULL))!=-1 || errno==EINTR) {
			if(errno!=EINTR) {
				logger << LogType::LOG_DEBUG_0 << "termino el proceso " << ret
						<< logger.endl;
			}
		}
	} else {
		logger << LogType::LOG_ERROR << "Error interrumpiendo procesos "
				"emisores y receptores creados por el ALC"
				<< logger.err << logger.endl;
	}
}

void _brokerClienteALC() {
	MiddlewareCliente& mw = MiddlewareClienteSingleton::instance();
	PDUPuntoAPunto pdu;
	DireccionTCP dir;
	TCPSocket sock;
	Direccion destino;

	forever {
		// obtiene un mensaje de la cola de entrada
		logger << LogType::LOG_DEBUG_0 << "Esperando mensaje en cola de salida..." << logger.endl;
		do {
			destino = (Direccion) mw.getAlc().recv(&pdu, 0, false, terminar);
		} while(!mw.getAlc() && errno==EINTR);

		if(!mw.getAlc()) {
			logger << LogType::LOG_ERROR <<"Error obteniendo mensaje de cola" << logger.err << logger.endl;
			terminarHijos();
			logger << LogType::LOG_DEBUG_0 << "alc terminado" << logger.endl;
			exit(0);
		}

		mw.getLineas().lock();

		// establece la línea de comunicación si es necesario
		if( !mw.getLineas().contains(destino)) {
			logger << LogType::LOG_DEBUG_0 << "Estableciendo línea de comunicación con destino " <<
					destino << logger.endl;
			dir = ConfigSingleton::instance().getHost(destino);
			if(dir.port>0) {
				sock = TCPSocket();
				if(sock.connect(dir.host, dir.port)) {
					logger << LogType::LOG_DEBUG_0 << "conectado, lanzando emisor" << logger.endl;
					if(brokerClienteReceptor(sock)!=(pid_t)-1) {
						logger << LogType::LOG_DEBUG_0 << "esperando handshake" << logger.endl;
						mw.getSem().wait();
					}
					sock.close();
				} else {
					logger << LogType::LOG_ERROR << "error conectando" << logger.err << logger.endl;
				}
			} else {
				logger << LogType::LOG_ERROR << "error traduciendo dirección" << logger.endl;
			}
		}

		// verifica que realmente haya podido levantar la línea
		if( mw.getLineas().contains(destino)) {
			// le pasa el mensaje al proceso emisor
			logger << LogType::LOG_DEBUG_0 << "pasando mensaje a emisor" << logger.endl;
			mw.getSnd().send(&pdu, (long)destino);
		} else {
			logger << LogType::LOG_ERROR << "No se pudo levantar línea de "
					"comunicación con host " << destino << logger.endl;
		}

		mw.getLineas().unlock();
	}
}
